/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uk.ac.bath.schemes;

import java.util.Arrays;
import uk.ac.bath.environment.MachineIF;
import uk.ac.bath.environment.Fitness;
import uk.ac.bath.environment.Scheme;
import uk.ac.bath.util.MyRandom;

/**
 *
 * @author pjl
 */
public class GAScheme implements Scheme {


 //   SaneSubGene genes[];
    private GAGeneWrapper[] pop;
    int generation;
 //   int iTrial = 0;
    private GABuilder builder;
    int nPop;
    private int numBreed;
//    private int trialsPerGeneration;
    int iNext;
    private int nElite;
    private GAGeneWrapper currentGene;

    /**
     *
     * @param nPop   population size
     * @param nBreed top nBreed get to have sex
     * @param nElite top nElite survve to next generation
     * @param builder make machines from the Genes
     * 
     */
    public GAScheme(int nPop, int nBreed, int nElite, GABuilder builder) {
        numBreed = nBreed;
//        if (3 * nBreed > nPop) {
//            throw new Exception(" Too many breeders max is" + nPop / 3);
//        }
   //     this.trialsPerGeneration = trailsPerGeneration;
        this.builder = builder;
        this.nPop   = nPop;
        this.nElite =  nBreed;

        pop = new GAGeneWrapper[nPop];
    }

 
    public void init() {

        iNext=0;
        generation = 0;
        for (int i = 0; i < nPop; i++) {
            pop[i].subgene = builder.createGene();
         
        }
       
    }

    public MachineIF nextMachine() {
        currentGene=pop[iNext];
        MachineIF net = builder.build(pop[iNext].subgene);    /*form the network*/
        return net;
    }


    private void nextGeneration() {

        generation++;
        /*get average fitness level.  Multiply by 100 to get more resolution*/
        iNext=0;
  
        Arrays.sort(pop);


        for (int i = nElite ; i < nPop; i++) {

            GAGeneWrapper a = pop[i];

            int nn = i;
            if (i == 0) {
                nn = numBreed;
            }


            GAGeneWrapper b = pop[MyRandom.nextInt(nn)];



            GAGeneWrapper a1 = pop[nPop - 2 * i - 1];
            GAGeneWrapper b1 = pop[nPop - 2 * i - 2];

            a1.fitness=b1.fitness=0.0f;

        }

        for (int i = numBreed; i < nPop; i++) {
            builder.mutate(pop[i].subgene);

        }

    }

  

    @Override
    public void endOfEvaluation(Fitness fitness) {

      
        if (fitness != null) {
            currentGene.fitness=fitness.getMaximumFitness();
            currentGene=null;
            iNext++;
            if (iNext >= nPop) {
                nextGeneration();
            }
        }
     
    }

    public String getStatus() {
        return "SaneGeneration:"+generation+"  ";
    }

    public String reportSetup() {
        return "Scheme: Sane nPop="+nPop+" nBreed="+numBreed+" nElite ="+nElite +"\n"+
                builder.reportSetup();
    }

    class GAGeneWrapper implements Comparable<GAGeneWrapper> {
        GAGeneIF subgene;
        float fitness;

        public int compareTo(GAGeneWrapper o) {
            return Float.compare(fitness,o.fitness);
        }

    }

}


